<?php

namespace PHPSpring\Core;

use PHPSpring\Annotation\Interceptor;

class AOP {

  public static function postInit ($target) {
    $className = get_class($target);
    $annos = RuntimeAnnotation::getAnnotations($className);
    if (empty($annos) || empty($annos['class'])) {
      return;
    }
    $className = $annos['class']['name'];
    $clz = $annos['class']['type'];
    foreach ($annos['class']['annos'] as $annoMetas) {
      if (!empty($annoMetas[2]['postInit'])) {
        $annoClz = $annoMetas[0];
        $args = $annoMetas[1] ?? array();
        $annoClz::postInit($target, ...$args);
      }
    }
  }

  public static function invoke($target, $method, $args) {
    $targetAnnos = RuntimeAnnotation::getAnnotations(get_class($target));

    $propertiesAnnos = (empty($targetAnnos) || empty($targetAnnos['properties'])) ? [] : $targetAnnos['properties'];
    foreach ($propertiesAnnos as $property => $annos) {
      if ($annos['annos'] && isset($annos['annos']['Autowired'])) {
        $rf = new \ReflectionProperty($target, $property);
        $rf->setAccessible(true);
        $rf->setValue($target, ProxyFactory::getProxy($annos['type']));
      }
    }
    $interceptors = self::getInterceptors($targetAnnos, $method);
    $ret0 = null;
    $ret = null;
    if (isset($interceptors[Interceptor::BEFORE])) {
      foreach ($interceptors[Interceptor::BEFORE] as $interceptor) {
        list($interceptorClz, $interceptorArgs) = $interceptor;
        $interceptorMethod = Interceptor::BEFORE;
        $ret0 = $interceptorClz::$interceptorMethod($interceptorArgs, $ret0, $target, $method, $args);
      }
    }
    if (isset($interceptors[Interceptor::REPLACE]) && !empty($interceptors[Interceptor::REPLACE])) {
      foreach ($interceptors[Interceptor::REPLACE] as $interceptor) {
        list($interceptorClz, $interceptorArgs) = $interceptor;
        $interceptorMethod = Interceptor::REPLACE;
        $ret0 = $interceptorClz::$interceptorMethod($interceptorArgs, $ret0, $target, $method, $args);
      }
      $ret = $ret0;
    } else {
      $ret = $target->$method(...$args);
    }
    $ret0 = $ret;
    if (isset($interceptors[Interceptor::AFTER])) {
      foreach ($interceptors[Interceptor::AFTER] as $interceptor) {
        list($interceptorClz, $interceptorArgs) = $interceptor;
        $interceptorMethod = Interceptor::AFTER;
        $ret0 = $interceptorClz::$interceptorMethod($interceptorArgs, $ret0, $target, $method, $args);
      }
    }
    if (isset($interceptors[Interceptor::FINALLY])) {
      foreach ($interceptors[Interceptor::FINALLY] as $interceptor) {
        list($interceptorClz, $interceptorArgs) = $interceptor;
        $interceptorMethod = Interceptor::FINALLY;
        $ret0 = $interceptorClz::$interceptorMethod($interceptorArgs, $ret0, $target, $method, $args);
      }
    }
    return $ret;
  }

  public static function getInterceptors ($targetAnnos, $method) {
    if (empty($targetAnnos)) {
      return array();
    }
    $interceptors = array();
    foreach (Interceptor::NAMES as $interceptor) {
      $interceptors[$interceptor] = array();
    }
    $clzAnnos = empty($targetAnnos['class']['annos']) ? array() : $targetAnnos['class']['annos'];
    foreach ($clzAnnos as $name => $anno) {
      foreach (Interceptor::NAMES as $interceptor) {
        if (!empty($anno[2][$interceptor])) {
          $interceptors[$interceptor][$name] = array($anno[0], $anno[1]);
        }
      }
    }
    if (!empty($targetAnnos['methods'])) {
      foreach ($targetAnnos['methods'] as $methodInfo) {
        if (empty($methodInfo['annos']) || $methodInfo['name'] != $method) {
          continue;
        }
        foreach ($methodInfo['annos'] as $name => $anno) {
          foreach (Interceptor::NAMES as $interceptor) {
            if (!empty($anno[2][$interceptor])) {
              $interceptors[$interceptor][$name] = array($anno[0], $anno[1]);
            }
          }
        }
      }
    }
    return $interceptors;
  }
}
